//
//  JSBind_SpiderMonkey.h
//  SPJSBind
//
//  Created by 云 浮 on 12-6-1.
//  Copyright (c) 2012年 __MyCompanyName__. All rights reserved.
//

#ifndef SPJSBind_JSBind_SpiderMonkey_h
#define SPJSBind_JSBind_SpiderMonkey_h

#include "ModuleDefine.h"
#include "JSManager.h"

namespace _SPIDERMONKEY_BIND {

enum _eConvertValueType {
    PROCESS_NULL    = 0,
    PROCESS_BOOL    ,
    PROCESS_INT     ,
    PROCESS_FLOAT   ,
    PROCESS_DOUBLE  ,
    PROCESS_STRING  ,
    PROCESS_VOID    ,
    PROCESS_CUSTOM  ,
//    CONVERT_INSTANCE,
} ;

    template <class T> jsval _ConvertCToJS( JSContext* cx,T val )
    {
        char pType = typeid(val).name()[0] ;
        switch(pType) 
        {
            case 'b': return BOOLEAN_TO_JSVAL( val );
            case 'i': return INT_TO_JSVAL(val);
            case 'f': return DOUBLE_TO_JSVAL(JS_NewDouble (cx,(jsdouble)val)) ;
            case 'd': return DOUBLE_TO_JSVAL(JS_NewDouble(cx,(jsdouble)val)) ;
        }
        return jsval(0) ;
    }
    template <class T> jsval _ConvertCToJS( JSContext* cx,T* val)
    {
        if (0 == val) {
            return jsval(0) ;
        }
        char type = typeid(val).name()[1];
        switch(type) 
        {
            case 'c':
            case 'k':
                return STRING_TO_JSVAL(JS_NewStringCopyZ(cx, (const char *)val)) ;
            case 'v':  return PRIVATE_TO_JSVAL(val) ;
        }
        return jsval(0) ;
    }

    template<class T>  T* _ConvertJSToC(jsval val, T*)
    {
        T* res ;
        if (JSVAL_IS_STRING(val))
        {
            int len =  strlen( JS_GetStringBytes(JSVAL_TO_STRING(val) ));
            memcpy(res, JS_GetStringBytes(JSVAL_TO_STRING(val) ), len);
        }
        else if (JSVAL_IS_OBJECT(val))
        {
            //        JSObject* pParam ;
            //        JS_ValueToObject(SP_JSBind::CJSManager::Instance()->GetContext(), val, &pParam) ;
            //        memcpy( res,(T*)JS_GetPrivate(SP_JSBind::CJSManager::Instance()->GetContext(), pParam),sizeof((T*)JS_GetPrivate(SP_JSBind::CJSManager::Instance()->GetContext(), pParam))  );
        }
//        else 
//            return false;
//        return true;
        return res ;
    }
    
    template<class T> T _ConvertJSToC(jsval val,T)
    {
        T res ;
        if (JSVAL_IS_BOOLEAN(val))
        {
            res = (T)JSVAL_TO_BOOLEAN(val);
        }
        else if( JSVAL_IS_INT(val) )
        {
            res = (T)JSVAL_TO_INT(val);
        }
        else if (JSVAL_IS_DOUBLE(val))
        {
//            res =  (T)JSVAL_TO_DOUBLE(val) ;
        }
//        else 
//            return false;
//        return true;
        return res ;
    }

#define _CONVERT_JS_TO_C(x, TYPE)               _ConvertJSToC(x, TYPE(0))
#define _CONVERT_C_TO_JS(cx, x)                 _ConvertCToJS(cx, x)

#define _DECLARE_JSCLASS(JSCLASS) \
public: \
    JSObject* m_pJSObject ; \
    static JSContext* s_pContext ; \
    static JSObject* s_pGlobalObject ; \
    static JSObject* s_pJSType ; \
    static JSClass CJSProxyClass ; \
    static JSPropertySpec s_arrJSProperties[] ; \
    static JSFunctionSpec s_arrJSMethods[] ; \
public: \
    static JSObject* JSInit(JSContext *cx, JSObject *obj) ; \
    JSCLASS(JSObject* obj) ; \
    JSCLASS(JSObject* obj, JSObject* parent) ; \
    static JSBool JSConstructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) ; \
    static void JSDestructor(JSContext *cx, JSObject *obj) ; \
    static JSBool JSGetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) ; \
    static JSBool JSSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) ;

#define _INITIALIZE_JSCLASS(JSCLASS) \
JSObject* JSCLASS::s_pJSType = 0 ; \
JSContext* JSCLASS::s_pContext = 0 ; \
JSObject* JSCLASS::s_pGlobalObject = 0 ; \
JSClass JSCLASS::CJSProxyClass = { #JSCLASS, JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, \
    JSCLASS::JSGetProperty, JSCLASS::JSSetProperty, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JSCLASS::JSDestructor } ; \
\
JSObject* JSCLASS::JSInit(JSContext *cx, JSObject *obj) { \
    s_pContext = cx ; \
    s_pGlobalObject = obj ; \
    return (s_pJSType = JS_InitClass(cx, obj, 0, &(JSCLASS::CJSProxyClass), \
                                    JSCLASS::JSConstructor, 0, JSCLASS::s_arrJSProperties, JSCLASS::s_arrJSMethods, 0, 0)) ; } \
\
JSCLASS::JSCLASS(JSObject* obj) { \
    __ASSERT(this->m_pJSObject) ; \
    this->m_pJSObject = obj ; \
    printf("JSCLASS::JSCLASS(JSObject* obj) [%x]\n", obj) ; \
    __ASSERT(JS_SetPrivate(JSCLASS::s_pContext, obj, this)) ; \
} \
JSCLASS::JSCLASS(JSObject* obj, JSObject* parent) { \
    __ASSERT(this->m_pJSObject) ; \
    this->m_pJSObject = obj ; \
    __ASSERT(JS_SetPrivate(JSCLASS::s_pContext, obj, this)) ; \
    __ASSERT(JS_SetParent(JSCLASS::s_pContext, obj, parent)) ; \
    printf("JSCLASS::JSCLASS(JSObject* obj, JSObject* parent) [%x][%x]\n", obj, parent) ; \
} \
\
JSBool JSCLASS::JSConstructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { \
    JSCLASS* p ; \
    if (0 == argc) { p = new JSCLASS(obj) ; } \
    else if (1 == argc) { p = new JSCLASS(obj, JSVAL_TO_OBJECT(argv[0])) ; } \
    *rval = PRIVATE_TO_JSVAL(p->m_pJSObject) ; \
    return JS_TRUE ; } \
\
void JSCLASS::JSDestructor(JSContext *cx, JSObject *obj) { \
    printf("enter destructor function \n") ; \
    JSCLASS* p = (JSCLASS*)JS_GetPrivate(cx, obj) ; delete p ; p = 0 ; }

#define _INITIALIZE_JSOBJECT(JSCLASS) \
    m_pJSObject = JS_NewObject(JSCLASS::s_pContext, &(JSCLASS::CJSProxyClass), 0, 0) ; \
    __ASSERT(JS_SetPrivate(JSCLASS::s_pContext, m_pJSObject, this)) ; \

#define _INITIALIZE_HAS_PARENT_JSOBJECT(JSCLASS, JSPARENT) \
    m_pJSObject = JS_NewObject(JSCLASS::s_pContext, &(JSCLASS::CJSProxyClass), 0, ((JSPARENT*)this)->m_pJSObject) ; \
    __ASSERT(JS_SetPrivate(JSCLASS::s_pContext, m_pJSObject, this)) ;

// declare bind property's enum value.
#define _DECLARE_JSPROPERTY_ENUM_BEGIN                  private: enum {
#define _DECLARE_JSPROPERTY_ENUM_END                    } ;
#define _DECLARE_JSPROPERTY_ENUM(ENUM_PROPERTY)         ENUM_PROPERTY ,

// get property.
#define _INITIALIZE_JSPROPERTY_GET_BEGIN(JSCLASS) \
JSBool JSCLASS::JSGetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { \
    if (JSVAL_IS_INT(id)) { JSCLASS* p = 0 ; \
    __ASSERT(p = (JSCLASS*)JS_GetPrivate(cx, obj)) ; switch (JSVAL_TO_INT(id)) {
#define _INITIALIZE_JSPROPERTY_GET_END      default: break ; }} return JS_TRUE ; }
#define _INITIALIZE_JSPROPERTY_GET(ENUM_PROPERTY, PROPERTY) \
    case ENUM_PROPERTY: { *vp = _CONVERT_C_TO_JS(cx, p->PROPERTY) ; } break ;

// set property.
#define _INITIALIZE_JSPROPERTY_SET_BEGIN(JSCLASS) \
JSBool JSCLASS::JSSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { \
    if (JSVAL_IS_INT(id)) { JSCLASS* p = 0 ; \
    __ASSERT(p = (JSCLASS*)JS_GetPrivate(cx, obj)) ; switch (JSVAL_TO_INT(id)) {
#define _INITIALIZE_JSPROPERTY_SET_END      default: break ; }} return JS_TRUE ; }
#define _INITIALIZE_JSPROPERTY_SET(ENUM_PROPERTY, PROPERTY, TYPE) \
    case ENUM_PROPERTY: { p->PROPERTY = _CONVERT_JS_TO_C(*vp, (TYPE)) ; } break ;

// bind property.
#define _INITIALIZE_JSPROPERTY_BEGIN(JSCLASS)               JSPropertySpec JSCLASS::s_arrJSProperties[] = {
#define _INITIALIZE_JSPROPERTY_END                          { 0 }, } ;
#define _INITIALIZE_JSPROPERTY(ENUM_PROPERTY, PROPERTY)     { #PROPERTY, ENUM_PROPERTY, JSPROP_ENUMERATE, },

// none property.
#define _NONE_JSPROPERTY(JSCLASS) \
    _INITIALIZE_JSPROPERTY_BEGIN(JSCLASS) _INITIALIZE_JSPROPERTY_END \
    JSBool JSCLASS::JSGetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { return JS_TRUE ; } \
    JSBool JSCLASS::JSSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { return JS_TRUE ; }

// convert member function to static function.
#define _DECLARE_NO_RETURN_STATIC_FUNCTION(JSCLASS, FUNCTION, PARAM_COUNT) \
static JSBool FUNCTION(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) { \
    JSCLASS* p = 0 ; __ASSERT(p = (JSCLASS*)JS_GetPrivate(cx, obj)) ; \
    switch (PARAM_COUNT) { \
    case 0: { p->FUNCTION() ; } break ; \
    case 1: { p->FUNCTION(_CONVERT_JS_TO_C(argv[0])) ; } break ; \
    case 2: { p->FUNCTION(_CONVERT_JS_TO_C(argv[0]), _CONVERT_JS_TO_C(argv[1]))} break ; \
    case 3: { p->FUNCTION(_CONVERT_JS_TO_C(argv[0]), _CONVERT_JS_TO_C(argv[1]), _CONVERT_JS_TO_C(argv[2]))} break ; \
    case 4: { p->FUNCTION(_CONVERT_JS_TO_C(argv[0]), _CONVERT_JS_TO_C(argv[1]), _CONVERT_JS_TO_C(argv[2]), _CONVERT_JS_TO_C(argv[3]))} break ; \
    case 5: { p->FUNCTION(_CONVERT_JS_TO_C(argv[0]), _CONVERT_JS_TO_C(argv[1]), _CONVERT_JS_TO_C(argv[2]), _CONVERT_JS_TO_C(argv[3]), _CONVERT_JS_TO_C(argv[4]))} break ; \
        default: break ; } \
    return JS_TRUE ; }

// declare static function.
#define _DECLARE_NO_RETURN_JSFUNCTION(JSCLASS, FUNCTION, PARAM_COUNT) \
static JSBool FUNCTION(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) { \
    switch (PARAM_COUNT) { \
    case 0: { JSCLASS::FUNCTION() ; } break ; \
    case 1: { JSCLASS::FUNCTION(_CONVERT_JS_TO_C(argv[0])) ; } break ; \
    case 2: { JSCLASS::FUNCTION(_CONVERT_JS_TO_C(argv[0]), _CONVERT_JS_TO_C(argv[1]))} break ; \
    case 3: { JSCLASS::FUNCTION(_CONVERT_JS_TO_C(argv[0]), _CONVERT_JS_TO_C(argv[1]), _CONVERT_JS_TO_C(argv[2]))} break ; \
    case 4: { JSCLASS::FUNCTION(_CONVERT_JS_TO_C(argv[0]), _CONVERT_JS_TO_C(argv[1]), _CONVERT_JS_TO_C(argv[2]), _CONVERT_JS_TO_C(argv[3]))} break ; \
    case 5: { JSCLASS::FUNCTION(_CONVERT_JS_TO_C(argv[0]), _CONVERT_JS_TO_C(argv[1]), _CONVERT_JS_TO_C(argv[2]), _CONVERT_JS_TO_C(argv[3]), _CONVERT_JS_TO_C(argv[4]))} break ; \
        default: break ; } \
    return JS_TRUE ; }

#define _DECLARE_NO_RETURN_STATIC_FUNCTION_1(JSCLASS, FUNCTION, PARAM_COUNT) \
static JSBool FUNCTION(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) { \
    JSCLASS* p = 0 ; __ASSERT(p = (JSCLASS*)JS_GetPrivate(cx, obj)) ; \
    switch (PARAM_COUNT) { \
        case 0: { p->FUNCTION() ; } break ; \
        default: break ; } \
    return JS_TRUE ; }

// bind function.
#define _INITIALIZE_JSFUNCTION_BEGIN(JSCLASS)                   JSFunctionSpec JSCLASS::s_arrJSMethods[] = {
#define _INITIALIZE_JSFUNCTION_END                              { 0 }, } ;
#define _INITIALIZE_JSFUNCTION(JSFUNCTION, PARAM_COUNT)   { ""#JSFUNCTION"", JSFUNCTION, PARAM_COUNT, 0, 0, },
//#define _INITIALIZE_JSFUNCTION(JSFUNCTION, PARAM_COUNT, FLAG)   { ""#FLAG""#JSFUNCTION"", JSFUNCTION, PARAM_COUNT, 0, 0, },

// none function.
#define _NONE_JSFUNCTION(JSCLASS)     _INITIALIZE_JSFUNCTION_BEGIN(JSCLASS)   _INITIALIZE_JSFUNCTION_END

}

#define _DELETE_JSOBJECT(JSCLASS) { delete m_pJSObject ; m_pJSObject = 0 ; }

// external defines:
#define CONVERT_JS_TO_C                     _CONVERT_JS_TO_C
#define CONVERT_C_TO_JS                     _CONVERT_C_TO_JS

#define CONVERT_C_NULL_TO_JSVAL             _CONVERT_C_NULL_TO_JSVAL

#define DECLARE_JSCLASS                     _DECLARE_JSCLASS
#define INITIALIZE_JSCLASS                  _INITIALIZE_JSCLASS
#define INITIALIZE_JSOBJECT                 _INITIALIZE_JSOBJECT
#define INITIALIZE_HAS_PARENT_JSOBJECT      _INITIALIZE_HAS_PARENT_JSOBJECT

#define DECLARE_JSPROPERTY_ENUM_BEGIN       _DECLARE_JSPROPERTY_ENUM_BEGIN
#define DECLARE_JSPROPERTY_ENUM             _DECLARE_JSPROPERTY_ENUM
#define DECLARE_JSPROPERTY_ENUM_END         _DECLARE_JSPROPERTY_ENUM_END

#define INITIALIZE_JSPROPERTY_GET_BEGIN     _INITIALIZE_JSPROPERTY_GET_BEGIN
#define INITIALIZE_JSPROPERTY_GET           _INITIALIZE_JSPROPERTY_GET
#define INITIALIZE_JSPROPERTY_GET_END       _INITIALIZE_JSPROPERTY_GET_END

#define INITIALIZE_JSPROPERTY_SET_BEGIN     _INITIALIZE_JSPROPERTY_SET_BEGIN
#define INITIALIZE_JSPROPERTY_SET           _INITIALIZE_JSPROPERTY_SET
#define INITIALIZE_JSPROPERTY_SET_END       _INITIALIZE_JSPROPERTY_SET_END

#define INITIALIZE_JSPROPERTY_BEGIN         _INITIALIZE_JSPROPERTY_BEGIN
#define INITIALIZE_JSPROPERTY               _INITIALIZE_JSPROPERTY
#define INITIALIZE_JSPROPERTY_END           _INITIALIZE_JSPROPERTY_END

#define DECLARE_NO_RETURN_JSFUNCTION        _DECLARE_NO_RETURN_JSFUNCTION
#define DECLARE_NO_RETURN_STATIC_FUNCTION   _DECLARE_NO_RETURN_STATIC_FUNCTION

#define INITIALIZE_JSFUNCTION_BEGIN         _INITIALIZE_JSFUNCTION_BEGIN
#define INITIALIZE_JSFUNCTION               _INITIALIZE_JSFUNCTION
#define INITIALIZE_JSFUNCTION_END           _INITIALIZE_JSFUNCTION_END

#define NONE_JSPROPERTY                     _NONE_JSPROPERTY
#define NONE_JSFUNCTION                     _NONE_JSFUNCTION

#endif

// for example:
/*
 * bind a class need x steps.
 class CExample
 {
 public:
    CExample() ;
    ~CExample() ;

 public:
    T function(T param) ;
 
 DECLARE_JSCLASS(CExample) -------------------------------- (bind js)

 } ;

 INITIALIZE_JSCLASS(CExample) ----------------------------- (bind js)

 CExample::CExample()
 {
    // todo.

    NEW_JSOBJECT(CExample) -------------------------------- (bind js)
 }

 CExample::~CExample()
 {
    // todo.

    DELETE_JSOBJECT(CExample) ----------------------------- (bind js)
 }

 T CExample::function(T param)
 {
 }

 */






























